package edu.uwm.cs552;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.List;
import java.util.Map;
import java.util.Observer;
import java.util.logging.Logger;

import javax.swing.SwingUtilities;

import edu.uwm.cs552.util.Pair;

/**
 * All the information about a game.
 */
public final class RailGame {

	public static Color[] playerColors = {
		Color.BLUE,
		Color.GREEN.darker(),
		Color.MAGENTA.darker(),
		Color.YELLOW.darker(),
		Color.DARK_GRAY,
	};
	
	public static final double STARTUP = 50;
	
	private final HexBoard board;
	private final Player[] players;
	
	/**
	 * Create a game using the given board with the given number of players
	 * @param b board to use
	 * @param numPlayers number of players to allocate
	 */
	public RailGame(HexBoard b, int numPlayers) {
		if (b == null) {
			throw new IllegalArgumentException("Game requires a non-null board");
		}
		if (numPlayers <= 0 || numPlayers > playerColors.length) {
			throw new IllegalArgumentException("bad number of players: " + numPlayers);
		}
		board = b;
		players = new Player[numPlayers];
		for (int i=0; i < players.length; ++i) {
			players[i] = new Player(this,i+1,playerColors[i],STARTUP);
		}
	}
	
	// TODO: Add methods so people can ask things about the board
	// or about players' activities on the board.  Also write
	// a method to top up time.
	
	public boolean canBuild(HexEdge edge) {
		Pair<HexCoordinate,HexCoordinate> hcs = edge.getHexagons();
		if(board.getTerrain(hcs.fst) == Terrain.WATER || 
				board.getTerrain(hcs.snd) == Terrain.WATER ||
				board.getTerrain(hcs.fst) == Terrain.LARGE ||
				board.getTerrain(hcs.snd) == Terrain.LARGE ||
				board.getBarrier(edge) == Barrier.BLOCK)
			return false;
		for (Player p : players) {
			if(p.beingBuiltOn(edge))
				return false;
		}
		return true;
	}
	
	private boolean hasIncomingTrack(HexCoordinate h) {
		for(Player p : players) {
			p.hasIncomingTrack(h);
		}
		return false;	
	}
	
	public HexBoard getBoard() {
		return board;
	}
	
	public boolean isAnotherPlayers(Player p, HexEdge edge) {
		for(Player player : players) {
			if(p.hasBuilt(edge) && !player.equals(p)) return true;
		}
		return false;
	}
	
	public boolean canMove(HexCoordinate position, HexCoordinate nextMove) {
		for(Player p : players) {
			if(p.getTrainPosition().equals(nextMove))
				return false;
		}
		return true;
	}
	
	public boolean compareTime(Player p) {
		int majority = (players.length/2) + 1;
		int actual = 0;
		for(Player player : players) {
			if(p.getTurn() - player.getTurn() >= 10)
				++actual;
		}
		if(actual >= majority) return true;
		return false;
	}

	/**
	 * Draw all players' actions according to a particular player's perspective.
	 * We assume the game board is already drawn.
	 * If the observer is null, then use the omniscient perspective, which sees
	 * everything.
	 * @param g graphics context to be used, must be a graphics2D object
	 * @param scale scale to draw board at
	 * @param observer perspective to take
	 */
	public void draw(Graphics g, double scale, Player observer) {
		if (observer != null) {
			g.setColor(observer.getPlayerColor());
			Composite originalComposite = ((Graphics2D)g).getComposite();
			for (Map.Entry<HexEdge, RailState> e : observer.getProposedTrack().entrySet()) {
				e.getValue().draw((Graphics2D)g, e.getKey(), scale);
			}
			for (Map.Entry<HexEdge, RailState> e : observer.getTrack().entrySet()) {
				e.getValue().draw((Graphics2D)g, e.getKey(), scale);
			}
			for (HexCoordinate h : observer.getTrain().getMovements()) {
				float alpha = 0.2f;
				int type = AlphaComposite.SRC_OVER; 
				AlphaComposite composite = AlphaComposite.getInstance(type, alpha);
				((Graphics2D)g).setComposite(composite);
				((Graphics2D)g).fillPolygon(h.toPolygon(scale));
			}
			((Graphics2D)g).setComposite(originalComposite);
			observer.getTrain().draw(g, scale);
		}
		else {
			for (Player p : players) {
				g.setColor(p.getPlayerColor());
				Composite originalComposite = ((Graphics2D)g).getComposite();
				for (Map.Entry<HexEdge, RailState> e : p.getProposedTrack().entrySet()) {
					e.getValue().draw((Graphics2D)g, e.getKey(), scale);
				}
				for (Map.Entry<HexEdge, RailState> e : p.getTrack().entrySet()) {
					e.getValue().draw((Graphics2D)g, e.getKey(), scale);
				}
				for (HexCoordinate h : p.getTrain().getMovements()) {
					float alpha = 0.2f;
					int type = AlphaComposite.SRC_OVER; 
					AlphaComposite composite = AlphaComposite.getInstance(type, alpha);
					((Graphics2D)g).setComposite(composite);
					((Graphics2D)g).fillPolygon(h.toPolygon(scale));
				}
				((Graphics2D)g).setComposite(originalComposite);
				p.getTrain().draw(g, scale);
			}
		}
	}
	
	// Fake being an observable
	
	public void addObserver(Observer arg0) {
		for (Player p : players) {
			p.addObserver(arg0);
		}
	}

	public void deleteObserver(Observer arg0) {
		for (Player p : players) {
			p.deleteObserver(arg0);
		}
	}

	public void deleteObservers() {
		for (Player p : players) {
			p.deleteObservers();
		}
	}

	public static void main(String[] args) {
		if (args.length != 2) {
			System.err.println("RailGame requires two arguments: game-board and number of players");
			System.exit(1);
		}
		final HexBoard board = new HexBoard();
		try {
			board.read(new BufferedReader(new FileReader(args[0])));
			final int numPlayers = Integer.parseInt(args[1]);
			final RailGame game = new RailGame(board,numPlayers);
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					PlayerFrame main = new PlayerFrame(game,null,board);
					main.setSize(800,600);
					main.setVisible(true);
					for (int i=0; i < numPlayers; ++i) {
						PlayerFrame pf = new PlayerFrame(game,game.getPlayer(i),board);
						pf.setSize(800,600);
						pf.setVisible(true);
					}
					//testing logger
					Logger.getLogger("Game").info("Game Message");
					for (int i=0; i < numPlayers; ++i) {
						Logger.getLogger("Game.Player" + (i + 1)).info("Player " + (i + 1) + " Message");
					}
				}
			});
		} catch (Exception e) {
			System.err.println(e.getLocalizedMessage());
			System.exit(1);
		}
	}

	protected Player getPlayer(int i) {
		if (i >= players.length)
			return null;
		return players[i];
	}
}
